﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace BenchmarkWatch.NET
{
    /// <summary>
    /// 基准计时器, 线程级别
    /// </summary>
    public class BenchmarkWatch : IDisposable
    {
        private const string METHOD_NAME_FORMAT = "{0}(at {1}:{2})";
        private const string METHOD_TO_METHOD_NAME_FORMAT = "{0} -> {1}";
        private const string RECORD_FORMAT = "{0} during took a total of {1}ms, and executed is {2} times, the avg a times is {3}ms";
        private const string Empty = "";

        private bool _disposed;
        private bool _disabled;
        private string _method;

        /// <summary>
        /// 计时器MAPS
        /// </summary>
        private readonly IDictionary<string, WatchRecord> _maps;

        protected Stopwatch Watch { get; private set; }

        public BenchmarkWatch(bool disabled = false, [CallerMemberName] string method = Empty, [CallerFilePath] string path = Empty, [CallerLineNumber] int num = 0)
        {
            _disposed = false;
            _disabled = disabled;
            _maps = new Dictionary<string, WatchRecord>();
            Watch = Stopwatch.StartNew();
            _method = StringBuilderCached.Rent().AppendFormat(METHOD_NAME_FORMAT, method, path, num).ToStringAndReturn();
        }

        /// <summary>
        /// 开启计时器标记
        /// </summary>
        public void Mark([CallerMemberName] string method = Empty, [CallerFilePath] string path = Empty, [CallerLineNumber] int num = 0)
        {
            if (_disabled) return;
            var currentMethod = StringBuilderCached.Rent().AppendFormat(METHOD_NAME_FORMAT, method, path, num).ToStringAndReturn();
            var methods = StringBuilderCached.Rent().AppendFormat(METHOD_TO_METHOD_NAME_FORMAT, _method, currentMethod).ToStringAndReturn();
            var ms = Pressed();
            _method = currentMethod;
            // TODO: 存储
            if (_maps.TryGetValue(methods, out var record))
            {
                var records = record + ms;
                _maps[methods] = records;
            }
            else
            {
                _maps.Add(methods, new WatchRecord(ms));
            }
        }

        /// <summary>
        /// 获取此刻计时并继续计时
        /// </summary>
        /// <returns></returns>
        private long Pressed()
        {
            if (Watch.IsRunning)
            {
                Watch.Stop();
            }
            var elapsedMilliseconds = Watch.ElapsedMilliseconds;
#if (NET35 || NET20)
            Watch.Reset();
            Watch.Start();
#else
            Watch.Restart();
#endif
            return elapsedMilliseconds;
        }

        public IEnumerable<string> Print()
        {
            foreach (var pair in _maps)
            {
                yield return StringBuilderCached.Rent().AppendFormat(RECORD_FORMAT, pair.Key, pair.Value.TotalsMilliseconds, pair.Value.Times, pair.Value.MillisecondsPer).ToStringAndReturn();
            }
        }

        #region Dispose

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _maps.Clear();
                    Watch = null;
                }

                _method = null;
                _disabled = true;
                _disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
